﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// HCL Channel 
    /// </summary>
    public enum HCLChannel
    {
        /// <summary>
        /// 
        /// </summary>
        H = 0,
        /// <summary>
        /// 
        /// </summary>
        C = 1,
        /// <summary>
        /// 
        /// </summary>
        L = 2
    }

    /// <summary>
    /// 
    /// </summary>
    [DebuggerDisplay("H: {m_H}, L: {m_L}, C: {m_C}")]
    public class HCL : AbstractColorSpace
    {
        protected double m_H = 0;
        protected double m_L = 0;
        protected double m_C = 0;

        /// <summary>
        /// Construct a new HCL
        /// </summary>
        /// <param name="_color"></param>
        public HCL(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HCL"/> class.
        /// </summary>
        /// <param name="_H">The _ H.</param>
        /// <param name="_C">The _ C.</param>
        /// <param name="_L">The _ L.</param>
        public HCL(double _H, double _C, double _L)
        {
            m_H = _H;
            m_C = _C;
            m_L = _L;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HCL"/> class.
        /// </summary>
        internal HCL()
        {
        }

        /// <summary>
        /// Use this constructor for cloning
        /// </summary>
        /// <param name="_hcl"></param>
        internal HCL(HCL _hcl)
        {
            this.m_Color = _hcl.m_Color;
            this.m_H = _hcl.m_H;
            this.m_L = _hcl.m_L;
            this.m_C = _hcl.m_C;
        }

        /// <summary>
        /// Enable casting into operator
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static explicit operator HCL(Color _color)
        {
            return new HCL(_color);
        }

        /// <summary>
        /// Calculate the values
        /// </summary>
        protected override void CalcValues()
        {
            double y0 = 100;
            double alpha = ColorFunctions.MaxChannel(m_Color) / ColorFunctions.MinChannel(m_Color) * (1 / y0);
            double gamma = 3; //3 is daylight, 10 is overcast day, 30 is very dark overcast day
            double Q = Math.Pow(Math.E, alpha * gamma);
            double R = m_Color.R;
            double G = m_Color.G;
            double B = m_Color.B;

            double H = Math.Atan(G - B / R - G);

            if ((R - G) < 0 && G - B >= 0) m_H = 180 + m_H;
            if ((R - G) < 0 && (G - B) >= 0) m_H = m_H - 180;
            if ((R - G) >= 0 && (G - B) >= 0) m_H = 2 / 3 * m_H;
            if ((R - G) >= 0 && (G - B) < 0) m_H = 4 / 3 * m_H;
            if ((R - G) < 0 && (G - B) >= 0) m_H = 180 + 4 / 3 * m_H;

            m_C = Math.Atan(R - G) + Math.Abs(G - B) + Math.Abs(B - R) / 3;

            m_L = (Q * ColorFunctions.MaxChannel(m_Color) + (1 - Q) * ColorFunctions.MinChannel(m_Color)) / 2;
        }

        /// <summary>
        /// Return or set h
        /// </summary>
        public double H
        {
            get { return m_H; }
            set { m_H = value; }
        }

        /// <summary>
        /// Return or set c
        /// </summary>
        public double C
        {
            get { return m_C; }
            set { m_C = value; }
        }

        /// <summary>
        /// return or set l
        /// </summary>
        public double L
        {
            get { return m_L; }
            set { m_L = value; }
        }

        /// <summary>
        /// Convert this value back to color
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            double dR = 0.0f;
            double dG = 0.0f;
            double dB = 0.0f;
            double Max = 255.0f;
            double Min = 0.0f;
            if (0 <= m_H && m_H <= 60) { dR = Max; dB = Min; dG = (dR * (Math.Tan(1.5f * m_H) + dB) / (1 + (Math.Tan(1.5f * m_H)))); }
            if (60 < m_H && m_H <= 120) { dG = Max; dB = Min; dR = (dG * (1 + Math.Tan(0.75f * m_H - 180)) - dB) / (Math.Tan(0.75f * (m_H - 180))); }
            if (120 < m_H && m_H <= 180) { dG = Max; dR = Min; dB = dG * (1 + Math.Tan(0.75f * (m_H - 180))) - dR * Math.Tan(0.75f * (m_H - 180)); }
            if (-60 <= m_H && m_H < 0) { dR = Max; dG = Min; dB = dG * (1 + Math.Tan(0.75f * m_H)) - dR * Math.Tan(0.75f * (m_H - 180)); }
            if (-120 <= m_H && m_H < -60) { dB = Max; dG = Min; dR = (dG * (1 + Math.Tan(0.75 * m_H)) - dB) / (Math.Tan(0.75 * m_H)); }
            if (-180 < m_H && m_H < 120) { dB = Max; dR = Min; dG = (dR * (Math.Tan(1.5f * (m_H + 180)) + dB)) / (1 + (Math.Tan(1.5f * (m_H + 180)))); }
            return Color.FromArgb((int)dR, (int)dG, (int)dB);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color H = {0}, C = {1}, L = {2}", m_H, m_C, m_L);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_H, m_C, m_L };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            this.m_H = (double)_aObject[0];
            this.m_C = (double)_aObject[1];
            this.m_L = (double)_aObject[2];
        }

        /// <summary>
        /// Clones this colorspace
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HCL(this);
        }
    }
}
